#!/usr/bin/python
#  Copyright (c) 2010
#  The Regents of the University of Michigan
#  All Rights Reserved

#  Permission is granted to use, copy, create derivative works, and
#  redistribute this software and such derivative works for any purpose,
#  so long as the name of the University of Michigan is not used in
#  any advertising or publicity pertaining to the use or distribution
#  of this software without specific, written prior authorization. If
#  the above copyright notice or any other identification of the
#  University of Michigan is included in any copy of any portion of
#  this software, then the disclaimer below must also be included.

#  This software is provided as is, without representation or warranty
#  of any kind either express or implied, including without limitation
#  the implied warranties of merchantability, fitness for a particular
#  purpose, or noninfringement.  The Regents of the University of
#  Michigan shall not be liable for any damages, including special,
#  indirect, incidental, or consequential damages, with respect to any
#  claim arising out of or in connection with the use of the software,
#  even if it has been or is hereafter advised of the possibility of
#  such damages.

import collections
import Gnuplot
import numpy
import os
import struct
import sys
import vuze_util

#import psyco
#psyco.full()

LOG_ENTRY = struct.Struct('20sIIH')

def log_iter(log_file_name):
  '''Invoke func for each log entry in the named log file.'''
  ret = []
  contents = open(log_file_name, 'rb').read()
  while len(contents) >= LOG_ENTRY.size:
    key, time, version, value_len = LOG_ENTRY.unpack(contents[:LOG_ENTRY.size])
    contents = contents[LOG_ENTRY.size:]
    value = contents[:value_len]
    contents = contents[value_len:]
    ret.append((key, time, version, value))
  return ret


def print_distribution(distribution, value_length):
  print 'Distribution for %d-byte values:' % value_length
  print 'Char\tCount'
  for idx, count in enumerate(distribution):
    print '%d\t%d' % (idx, count)


def plot_distribution(distribution, title, output_name):
  g = Gnuplot.Gnuplot()
  g.title(title)
  g('set style data histogram')
  g('set output "%s"' % output_name)
  g('set terminal png')
  g.plot(distribution)
  g('set terminal pop')


def plot_byte_distribution(distribution, value_length):
  plot_distribution(distribution,
                    'Byte distribution for %d-byte values' % value_length,
                    '%03d.png' % value_length)


def plot_store_request_distance(distribution, t0, tmax, node_name):
  # distribution: n_shared_bits -> time -> count
  title = 'Store request key distance from node ID'
  output_name = 'distance%s.png' % node_name
  datasets = []
  for (num_shared_bits, times) in distribution.iteritems():
    if times:
      datasets.append(Gnuplot.Data([(x-t0, times[x])
                                    for x in sorted(times.iterkeys())],
                                   title='%d shared bits' % num_shared_bits))
  g = Gnuplot.Gnuplot()
  g.title(title)
  g('set style data linespoints')
  g('set output "%s"' % output_name)
  g('set terminal png')
  g.plot(*datasets)
  g('set terminal pop')


def plot_arrival_times(arrival_times, name):
  g = Gnuplot.Gnuplot()
  total_arrivals = float(sum(arrival_times.itervalues()))
  t0 = min(arrival_times.iterkeys())
  tmax = max(arrival_times.iterkeys())
  arrivals = [(x-t0, arrival_times[x]) for x in sorted(arrival_times.iterkeys())]
  cdf = [(k, sum(y for (x,y) in arrivals[0:idx+1]) / total_arrivals) for (idx, (k, v)) in enumerate(arrivals)]
  
  g.title('CDF of packet arrivals for %s' % name)
  g('set xrange [0:]')
  g('set yrange [0:1]')
  g('set xlabel "Time since first arrival[s]"')
  g('set ylabel "Fraction of total packets arrived at time"')
  d = Gnuplot.Data(cdf)
  g('set output "arrivals%s.png"' % name)
  g('set terminal png')
  g.plot(d)
  g('set terminal pop')
  

def main():
  grand_total_value_size = 0
  total_num_values = 0

  def make_byte_distribution():
    return [0]*256

  value_byte_distributions = collections.defaultdict(make_byte_distribution)

  node_ip = sys.argv[1]

  for log in sys.argv[2:]:
    keys = []
    value_sizes = []
    arrival_times = collections.defaultdict(int) # default zero
    port = os.path.basename(log)[len('stores'):-len('.log')]
    
    # Map n_shared_bits -> time -> count
    keys_with_k_shared_prefix_bits = collections.defaultdict(lambda: collections.defaultdict(int))

    print log
    log_list = log_iter(log)
    if not log_list:
      continue
    node_id = vuze_util.compute_node_id(node_ip, int(port))

    for (key, time, version, value) in log_list:
      keys.append(key)
      value_sizes.append(len(value))

      arrival_times[time] += 1

      shared_bits = vuze_util.compute_shared_prefix_bits(key, node_id)
      keys_with_k_shared_prefix_bits[shared_bits == 8 and 8 or 16][time / 60] += 1
      
      distribution = value_byte_distributions[len(value)]
      for byte in value:
        distribution[ord(byte)] += 1

    total_value_size = sum(value_sizes)
    grand_total_value_size += total_value_size
    total_num_values += len(value_sizes)
#    print '%d keys with %d values' % (len(keys), len(value_sizes))
#    print 'Value bytes: %d' % total_value_size
#    if value_sizes:
#      print 'Average value size: %f' % (float(total_value_size) / len(value_sizes))


    if arrival_times:
      plot_arrival_times(arrival_times, port)
#    plot_store_request_distance(keys_with_k_shared_prefix_bits, min(arrival_times.iterkeys()) / 60, max(arrival_times.iterkeys()) / 60, port)


  print 'Overall total value bytes: %d' % grand_total_value_size
  print 'Overall average value size: %f' % (float(grand_total_value_size) / total_num_values)

#  print 'Value histograms:'
#  print '='*60


  # TODO: plot the value size distribution itself.


#   for value_length, distribution in sorted(value_byte_distributions.items()):
#     if value_length:
#       plot_byte_distribution(distribution, value_length)


if __name__ == '__main__':
  main()
